package io.gitee.minelx.commontools.multithread.processcontrol;

import java.util.concurrent.Semaphore;

public class ClockSemaphore implements SemaphoreHelper {

	private final Semaphore semaphore;

	private final int maxPermits;

	private final int permitsReleasingInterval;

	private final Object lastTimeFullReleasingLock;

	private long lastTimeFullReleasing;

	ClockSemaphore(int maxPermits, int permitsReleasingInterval) {
		this.maxPermits = maxPermits;
		this.permitsReleasingInterval = permitsReleasingInterval;
		semaphore = new Semaphore(maxPermits, true);
		lastTimeFullReleasingLock = new Object();
		lastTimeFullReleasing = System.currentTimeMillis();
	}

	@Override
	public void release() {
		releaseAcquiredPermit();
	}

	@Override
	public void acquire() {
		fullyReleaseIfNeeded();
		// acquire a permit before download
		acquirePermit();
	}


	private void fullyReleaseIfNeeded() {
		long now = System.currentTimeMillis();
		synchronized (lastTimeFullReleasingLock) {
			if (!neededFullyRelease(now)) {
				// need no releasing
				return;
			}
			lastTimeFullReleasing = now;
		}
		// release permits fully
		semaphore.drainPermits();
		semaphore.release(maxPermits);
	}

	private void acquirePermit() {
		try {
			semaphore.acquire();
		} catch (InterruptedException e) {
			throw new RuntimeException("error while acquiring permit from semaphore.", e);
		}
	}

	private void releaseAcquiredPermit() {
		semaphore.release();
	}

	private boolean neededFullyRelease(long now) {
		long howLongItPast = now - lastTimeFullReleasing;
		// it's time to release some permits
		return howLongItPast >= permitsReleasingInterval;
	}
}
